/**
 * AdvancedPatternOptTrim.java
 *
 * Created on 27 Март 2008 г., 23:35
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
package trim.calc;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import trim.TrimException;
import trim.calc.smdata.ExtensionTrimSMData;
import trim.common.bp.BPException;
import trim.common.bp.BestPartition;
import trim.common.inputdata.InputData;
import trim.common.result.TrimResult;
import trim.common.sm.SMColumn;
import trim.common.sm.SMException;
import trim.common.sm.TrimSimplex;

/**
 * Алгоритм с улучшенным перебором базисов.
 * СМ+расширенный СМ+оптимизация СМ по перенастройкам ПРС+перебор
 * (Оптимизация СМ: с помошью итерации СМ вводится в базис столбец,
 * не увеличивающий число различных способов раскроя)
 *
 * @author Дмитрий
 */
public class AdvancedPatternOptTrim extends SMExtensionTrim
{

  /** Creates a new instance of AdvancedPatternOptTrim */
  public AdvancedPatternOptTrim()
  {
  }

  @Override
  public TrimResult solve(InputData inputData) throws TrimException
  {
    try
    {
      inputData=inputData.clone();
      TrimSimplex sm=extensionSimplex(inputData);
      SMOptimization(sm);
      TrimResult tr=sm.getTrimResult();
      inputData.getFormatOrderList().clear();
      inputData.getFormatOrderList().addAll(sm.getRestFormatCount());
      inputData.getFormatOrderList().remove(inputData.getFormatOrderList().size()-1);
      int lt=sm.getRestMaterials();
      tr.addAll(new BestPartition(inputData).solve(lt));
      return tr;
    }catch(BPException ex)
    {
      throw new TrimException("Error in BP",ex);
    }catch(SMException ex)
    {
      throw new TrimException("Error in SM",ex);
    }

  }

  /**
   * Минимизация перенастроек ножей
   * @param sm - СМ
   * @throws trim.common.sm.SMException
   */
  protected void SMOptimization(TrimSimplex sm) throws SMException
  {
    System.out.println(this.getClass().getSimpleName()+": Run SMOptimization");
    ExtensionTrimSMData smData=(ExtensionTrimSMData)sm.getSmData();
    System.out.println(this.getClass().getSimpleName()+": Getting zerro cols...");
    List<SMColumn> zcols=smData.getZeroCols(sm.getDualVariables());
    System.out.println(this.getClass().getSimpleName()+": zcols number="+zcols.size());
    System.out.println(this.getClass().getSimpleName()+": Run MinCutting");
    MinCutting(sm,zcols);
  }

  /**
   * Алгоритм минимизации перенастроек ножей
   * @param sm - СМ
   * @param zcols - список столбцов с нулевой оценкой
   * @throws trim.common.sm.SMException в случае, если переход к
   * очередному выбранному столбцу невозможен (такого быть не должно)
   */
  private void MinCutting(TrimSimplex sm,List<SMColumn> zcols) throws SMException
  {

    int i, j, k, l;
    int n=sm.getSmData().getProblemSize();
    int rec;//текущее значение цел ф-ции
    double fr;
    int rec1, rec2;//для поиска вводимого вектора
    double fr1, fr2;
    int nm;//номер найденного вектора
    boolean Opt=false;//оптимально ли решение
    double f, ffix;//значение целевой функции
    rec=0;//кличество нулей
    fr=0.0;//дробная часть
    ffix=0.0;
    double[] res=sm.getObjectiveFunction();
    for(j=0;j<n;j++)
    {
      if(Math.abs(res[j])>=1-2e-10)
        rec++;
      fr+=res[j]-Math.floor(res[j]+2e-10);
      ffix+=res[j];
    }
    System.out.println(this.getClass().getSimpleName()+": Start singularity="+(rec+fr));
    int m=zcols.size();
    int cc=0;
    int icount=0;
    while(!Opt)
    {
      //поиск вектора для уменьшения целевой функции
      rec1=rec;
      fr1=fr;
      nm=-1;
      cc=0;
      k=-1;
      for(k=0;k<m;k++)
      {
        rec2=0;//количество нулей
        fr2=0.0;//дробная часть
        f=0.0;
        double[] Resmin=null;
        try
        {
          Resmin=sm.testNextBase(zcols.get(k));
        }catch(Exception ex)
        {
          ex.printStackTrace();
          continue;
        }
        for(j=0;j<n;j++)
        {
          if(Math.abs(Resmin[j])>=1-2e-10)
            rec2++;
          fr2+=Resmin[j]-Math.floor(Resmin[j]+2e-10);
          f+=Resmin[j];
        }
        if(Math.abs(f-ffix)>1e-10)
          continue;

        //if(rec2>rec1||(rec1==rec2&&fr1-fr2>=1e-15))
        if(rec1+fr1-rec2-fr2>1e-13)
        {
          rec1=rec2;
          fr1=fr2;
          nm=k;
          //if(rec1-rec1)
            k=m+1;

        }
        if(Math.abs(rec+fr-rec1-fr1)<0.1)
          cc++;
        else
          cc=0;

      }
      if(nm>=0)
      {
        sm.NextBase(zcols.get(nm));
        rec=rec1;
        fr=fr1;
        icount++;
        if(fr<1e-12)
          Opt=true;
      }else
      {
        Opt=true;
      }
    }
    System.out.println(this.getClass().getSimpleName()+": iteration count="+icount);
  System.out.println(this.getClass().getSimpleName()+": Final singularity="+(rec+fr));
//f.close();

    //for(i=0;i<n;i++)Res[i]=Resmin[i];

    /*	for(i=0;i<n;i++)
    for(j=0;j<n;j++)X[i][j]=Xmin[i][j];*/


    return;
  }

  @Override
  public String getName()
  {
    return this.getClass().getSimpleName();
  }

  @Override
  public String getVersion()
  {
    InputStream is=getClass().getResourceAsStream("/trim/calc/version");
    byte[] b=new byte[20];
    try
    {
      int n=is.read(b);
      return new String(b,0,n);
    }catch(IOException ex)
    {
      ex.printStackTrace();
      return "0";
    }
  }

  @Override
  public String getDescription()
  {
    return "Комбинированный алгоритм решения ЗФР с "+
        "оптимизацией перенастроек ПРС";
  }
}
